/****************************************************************************
 * fs/nfs/nfs_vfsops.c
 *
 *   Copyright (C) 2012-2013, 2015 Gregory Nutt. All rights reserved.
 *   Copyright (C) 2012 Jose Pablo Rojas Vargas. All rights reserved.
 *   Author: Jose Pablo Rojas Vargas <jrojas@nx-engineering.com>
 *           Gregory Nutt <gnutt@nuttx.org>
 *   Copyright (c) <2013-2015>, <Huawei Technologies Co., Ltd>
 *   All rights reserved.
 *
 * Leveraged from OpenBSD:
 *
 *   Copyright (c) 1989, 1993, 1995
 *   The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Rick Macklem at The University of Guelph.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 ****************************************************************************/

/**********************************************************************************
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which
 * might include those applicable to Huawei LiteOS of U.S. and the country in which you
 * are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance
 * with such applicable export control laws and regulations.
 **********************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/
#include "vfs_config.h"
#include "errno.h"
#include "sys/mount.h"
#include <sys/socket.h>
#include <sys/statfs.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <semaphore.h>
#include <assert.h>
#include <debug.h>
#include <unistd.h>
#include "dirent.h"
#include "fs/fs.h"
#include "fs/dirent_fs.h"
#include "lwip/opt.h"
#include "lwip/sockets.h"
#include "nfs.h"
#include "nfs_node.h"
#include "xdr_subs.h"
#include "los_tables.h"
#include "inode/inode.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/
#ifdef LOSCFG_FS_NFS
/* The V3 EXCLUSIVE file creation logic is not fully supported. */

#define USE_GUARDED_CREATE    1

/* include/fs/dirent.h has its own version of these lengths.  They must
 * match the NFS versions.
 */

#if NFSX_V3FHMAX != DIRENT_NFS_MAXHANDLE
#  error "Length of file handle in fs_dirent_s is incorrect"
#endif

#if NFSX_V3COOKIEVERF != DIRENT_NFS_VERFLEN
#  error "Length of cookie verify in fs_dirent_s is incorrect"
#endif

#define MIN_IPv4_UDP_MSS (IP_FRAG_MAX_MTU - (14 + 20)) /* (Ethernet MTU - (Eth header size + IP header size)) */

/****************************************************************************
 * Public Variables
 ****************************************************************************/

uint32_t nfs_true;
uint32_t nfs_false;
NFSMOUNT_HOOK g_NFSMOUNT_HOOK = (NFSMOUNT_HOOK)NULL; /*lint !e611*/

#ifdef CONFIG_NFS_STATISTICS
struct nfsstats nfsstats;
#endif

/****************************************************************************
 * Private Type Definitions
 ****************************************************************************/

#define NFS_DIR_ENTRY_MALLOC(entry) do{ \
entry = (struct entry3 *)malloc(sizeof(struct entry3)); \
if (entry == NULL) \
{ \
   fvdbg("malloc failed\n"); \
   error = ENOMEM; \
   goto errout_with_memory; \
} \
(void)memset_s(entry, sizeof(struct entry3), 0, sizeof(struct entry3)); \
}while(0)

#define NFS_DIR_ENTRY_FREE(entry) do{ \
free(entry->contents); \
entry->contents = NULL; \
free(entry); \
entry = NULL; \
}while(0)


/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

static int     nfs_filecreate(FAR struct nfsmount *nmp, struct nfsnode *np, FAR const char *relpath, int oflags, mode_t mode);
static int     nfs_filetruncate(FAR struct nfsmount *nmp, struct nfsnode *np);
static int     nfs_fileopen(FAR struct nfsmount *nmp, struct nfsnode *np, FAR const char *relpath, int oflags, mode_t mode);
static int     nfs_open(FAR struct file *filep, const char *relpath, int oflags, mode_t mode);
static int     nfs_close(FAR struct file *filep);
static ssize_t nfs_read(FAR struct file *filep, char *buffer, size_t buflen);
static ssize_t nfs_write(FAR struct file *filep, const char *buffer, size_t buflen);
static off_t   nfs_seek(FAR struct file *filep, off_t offset, int whence);
static loff_t   nfs_seek64(FAR struct file *filep, loff_t offset, int whence);
static int     nfs_dup(FAR const struct file *oldp, FAR struct file *newp);
static int     nfs_opendir(struct inode *mountpt, const char *relpath, struct fs_dirent_s *dir);
static int      nfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir);
static int     nfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir);
static int     nfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir);
static void    nfs_decode_args(FAR struct nfs_mount_parameters *nprmt, FAR struct nfs_args *argp);
static int     nfs_bind(FAR struct inode *blkdriver, const void *data, void **handle, FAR const char *realpath);
static int     nfs_unbind(void *handle, FAR struct inode **blkdriver);
static int     nfs_statfs(struct inode *mountpt, struct statfs *buf);
static int     nfs_remove(struct inode *mountpt, const char *relpath);
static int     nfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode);
static int     nfs_rmdir(struct inode *mountpt, const char *relpath);
static int     nfs_rename(struct inode *mountpt, const char *oldrelpath, const char *newrelpath);
static int     nfs_stat(struct inode *mountpt, const char *relpath, struct stat *buf);
static int     nfs_stat64(struct inode *mountpt, const char *relpath, struct stat64 *buf);


/****************************************************************************
 * Public Data
 ****************************************************************************/
/* nfs vfs operations. */

const struct mountpt_operations nfs_operations =
{ /*lint -e64*/
    nfs_open,                     /* open */
    nfs_close,                    /* close */
    nfs_read,                     /* read */
    nfs_write,                    /* write */
    nfs_seek,                     /* seek */
    NULL,                         /* ioctl */

    NULL,                         /* sync */
    nfs_dup,                      /* dup */

    nfs_opendir,                  /* opendir */
    nfs_closedir,                 /* closedir */
    nfs_readdir,                  /* readdir */
    nfs_rewinddir,                /* rewinddir */

    nfs_bind,                     /* bind */
    nfs_unbind,                   /* unbind */
    nfs_statfs,                   /* statfs */
    NULL,                         /* virstatfs */
    nfs_remove,                   /* unlink */
    nfs_mkdir,                    /* mkdir */
    nfs_rmdir,                    /* rmdir */
    nfs_rename,                   /* rename */
    nfs_stat,                     /* stat */
    nfs_stat64,                   /* stat64 */
    NULL,                         /* for utime*/
    NULL,                         /* chattr */
    nfs_seek64,                   /* seek64 */
    NULL,                         /*getlabel*/
    NULL,                         /* fallocate */
    NULL,                         /* fallocate64 */
    NULL,                         /* truncate */
    NULL,                         /* truncate64 */
    NULL                           /* fscheck */
}; /*lint +e64*/

FSMAP_ENTRY(nfs_fsmap, "nfs", nfs_operations, FALSE, FALSE)

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: nfs_filecreate
 *
 * Description:
 *   Create a file.  This is part of the file open logic that is executed if
 *   the user asks to create a file.
 *
 * Returned Value:
 *   0 on success; a positive errno value on failure.
 *
 ****************************************************************************/

static int nfs_filecreate(FAR struct nfsmount *nmp, struct nfsnode *np,
        FAR const char *relpath, int oflags, mode_t mode)
{
    struct file_handle      fhandle;
    struct nfs_fattr        fattr;
    char                    filename[NAME_MAX + 1];
    FAR uint32_t           *ptr;
    uint32_t                tmp;
    size_t                  namelen;
    int                     reqlen;
    int                     error;

    /* Find the NFS node of the directory containing the file to be created */

    error = nfs_finddir(nmp, relpath, &fhandle, &fattr, filename);
    if (error != OK)
    {
        fdbg("ERROR: nfs_finddir returned: %d\n", error);
        return error;
    }

    /* Create the CREATE RPC call arguments */

    ptr    = (FAR uint32_t *)&nmp->nm_msgbuffer.create.create;
    reqlen = 0;

    /* Copy the variable length, directory file handle */

    *ptr++  = txdr_unsigned(fhandle.length);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, fhandle.length, &fhandle.handle, fhandle.length);
    reqlen += (int)fhandle.length;
    ptr    += uint32_increment(fhandle.length);

    /* Copy the variable-length file name */

    namelen = strlen(filename);

    *ptr++  = txdr_unsigned(namelen);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, sizeof(filename), filename, namelen);
    ptr    += uint32_increment(namelen);
    reqlen += uint32_alignup(namelen);

    /* Set the creation mode */

#ifdef USE_GUARDED_CREATE
    *ptr++  = htonl(NFSV3CREATE_GUARDED);
#else
    *ptr++  = htonl(NFSV3CREATE_EXCLUSIVE);
#endif
    reqlen += sizeof(uint32_t);

    /* Mode information is not provided if EXCLUSIVE creation is used.
     * in this case, we must call SETATTR after successfully creating
     * the file.
     */

#ifndef USE_GUARDED_CREATE
    if ((oflags & O_CREAT) == 0)
#endif
    {
        /* Set the mode.  NOTE: Here we depend on the fact that the NuttX and NFS
         * bit settings are the same (at least for the bits of interest).
         */

        *ptr++  = nfs_true; /* True: mode value follows */
        reqlen += sizeof(uint32_t);

        tmp = mode & (NFSMODE_IWOTH | NFSMODE_IROTH | NFSMODE_IWGRP |
                NFSMODE_IRGRP | NFSMODE_IWUSR | NFSMODE_IRUSR);
        *ptr++  = txdr_unsigned(tmp);
        reqlen += sizeof(uint32_t);

        /* Set the user ID to zero */

        *ptr++  = nfs_true;             /* True: Uid value follows */
        *ptr++  = 0;                    /* UID = 0 (nobody) */
        reqlen += 2*sizeof(uint32_t);

        /* Set the group ID to one */

        *ptr++  = nfs_true;            /* True: Gid value follows */
        *ptr++  = htonl(1);            /* GID = 1 (nogroup) */
        reqlen += 2*sizeof(uint32_t);

        /* Set the size to zero */

        *ptr++  = nfs_true;            /* True: Size value follows */
        *ptr++  = 0;                   /* Size = 0 */
        *ptr++  = 0;
        reqlen += 3*sizeof(uint32_t);

        /* Don't change times */

        *ptr++  = htonl(NFSV3SATTRTIME_DONTCHANGE); /* Don't change atime */
        *ptr++  = htonl(NFSV3SATTRTIME_DONTCHANGE); /* Don't change mtime */
        reqlen += 2*sizeof(uint32_t);
    }

    /* Send the NFS request.  Note there is special logic here to handle version 3
     * exclusive open semantics.
     */

    do
    {
        nfs_statistics(NFSPROC_CREATE);
        error = nfs_request(nmp, NFSPROC_CREATE,
                (FAR void *)&nmp->nm_msgbuffer.create, reqlen,
                (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
    }
#ifdef USE_GUARDED_CREATE
    while (0);
#else
    while (((oflags & O_CREAT) != 0) && error == EOPNOTSUPP);
#endif

    /* Check for success */

    if (error == OK)
    {
        /* Parse the returned data */

        ptr = (FAR uint32_t *)&((FAR struct rpc_reply_create *)nmp->nm_iobuffer)->create;

        /* Save the file handle in the file data structure */

        tmp = *ptr++;  /* handle_follows */
        if (!tmp)
        {
            fdbg("ERROR: no file handle follows\n");
            return EINVAL;
        }

        tmp = *ptr++;
        tmp = fxdr_unsigned(uint32_t, tmp);
        DEBUGASSERT(tmp <= NFSX_V3FHMAX);

        np->n_fhsize      = (uint8_t)tmp;
        (void)memcpy_s(&np->n_fhandle, np->n_fhsize, ptr, tmp);
        ptr += uint32_increment(tmp);

        /* Save the attributes in the file data structure */

        tmp = *ptr;  /* handle_follows */
        if (!tmp)
        {
            fdbg("WARNING: no file attributes\n");
        }
        else
        {
            /* Initialize the file attributes */

            nfs_attrupdate(np, (FAR struct nfs_fattr *)ptr);
        }

        /* Any following dir_wcc data is ignored for now */
    }

    return error;
}

/****************************************************************************
 * Name: nfs_fileopen
 *
 * Description:
 *   Truncate an open file to zero length.  This is part of the file open
 *   logic.
 *
 * Returned Value:
 *   0 on success; a positive errno value on failure.
 *
 ****************************************************************************/

static int nfs_filetruncate(FAR struct nfsmount *nmp, struct nfsnode *np)
{
    FAR uint32_t *ptr;
    int           reqlen;
    int           error;

    fvdbg("Truncating file\n");

    /* Create the SETATTR RPC call arguments */

    ptr    = (FAR uint32_t *)&nmp->nm_msgbuffer.setattr.setattr;
    reqlen = 0;

    /* Copy the variable length, directory file handle */

    *ptr++  = txdr_unsigned((uint32_t)np->n_fhsize);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, np->n_fhsize, &np->n_fhandle, np->n_fhsize);
    reqlen += (int)np->n_fhsize;
    ptr    += uint32_increment(np->n_fhsize);

    /* Copy the variable-length attributes */

    *ptr++  = nfs_false;                        /* Don't change mode */
    *ptr++  = nfs_false;                        /* Don't change uid */
    *ptr++  = nfs_false;                        /* Don't change gid */
    *ptr++  = nfs_true;                         /* Use the following size */
    *ptr++  = 0;                                /* Truncate to zero length */
    *ptr++  = 0;
    *ptr++  = htonl(NFSV3SATTRTIME_TOSERVER);   /* Use the server's time */
    *ptr++  = htonl(NFSV3SATTRTIME_TOSERVER);   /* Use the server's time */
    *ptr++  = nfs_false;                        /* No guard value */
    reqlen += 9 * sizeof(uint32_t);

    /* Perform the SETATTR RPC */

    nfs_statistics(NFSPROC_SETATTR);
    error = nfs_request(nmp, NFSPROC_SETATTR,
            (FAR void *)&nmp->nm_msgbuffer.setattr, reqlen,
            (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
    if (error != OK)
    {
        fdbg("ERROR: nfs_request failed: %d\n", error);
        return error;
    }

    /* Indicate that the file now has zero length */

    np->n_size = 0;
    return OK;
}

/****************************************************************************
 * Name: nfs_fileopen
 *
 * Description:
 *   Open a file.  This is part of the file open logic that attempts to open
 *   an existing file.
 *
 * Returned Value:
 *   0 on success; a positive errno value on failure.
 *
 ****************************************************************************/

static int nfs_fileopen(FAR struct nfsmount *nmp, struct nfsnode *np,
        FAR const char *relpath, int oflags, mode_t mode)
{
    struct file_handle fhandle;
    struct nfs_fattr   fattr;
    uint32_t           tmp;
    int                error = 0;

    /* Find the NFS node associate with the path */

    error = nfs_findnode(nmp, relpath, &fhandle, &fattr, (struct nfs_fattr *)NULL);
    if (error != OK)
    {
        fdbg("ERROR: nfs_findnode returned: %d\n", error);
        return error;
    }

    /* Check if the object is a directory */

    tmp = fxdr_unsigned(uint32_t, fattr.fa_type);
    if (tmp == NFDIR)
    {
        /* Exit with EISDIR if we attempt to open a directory */

        fdbg("ERROR: Path is a directory\n");
        return EISDIR;
    }

    /* Check if the caller has sufficient privileges to open the file */

    if ((oflags & O_WRONLY) != 0)
    {
        /* Check if anyone has priveleges to write to the file -- owner,
         * group, or other (we are probably "other" and may still not be
         * able to write).
         */

        tmp = fxdr_unsigned(uint32_t, fattr.fa_mode);
        if ((tmp & (NFSMODE_IWOTH|NFSMODE_IWGRP|NFSMODE_IWUSR)) == 0)
        {
            fdbg("ERROR: File is read-only: %08x\n", tmp);
            return EACCES;
        }
    }

    /* It would be an error if we are asked to create the file exclusively */

    if ((oflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
    {
        /* Already exists -- can't create it exclusively */

        fdbg("ERROR: File exists\n");
        return EEXIST;
    }

    /* Initialize the file private data */
    /* Copy the file handle */

    np->n_fhsize      = (uint8_t)fhandle.length;
    (void)memcpy_s(&np->n_fhandle, np->n_fhsize, &fhandle.handle, fhandle.length);

    /* Save the file attributes */

    nfs_attrupdate(np, &fattr);

    /* If O_TRUNC is specified and the file is opened for writing,
     * then truncate the file.  This operation requires that the file is
     * writable, but we have already checked that. O_TRUNC without write
     * access is ignored.
     */

    if ((oflags & O_TRUNC) && (oflags & (O_WRONLY | O_RDWR)))
    {
        /* Truncate the file to zero length.  I think we can do this with
         * the SETATTR call by setting the length to zero.
         */

        return nfs_filetruncate(nmp, np);
    }

    return OK;
}


/****************************************************************************
 * Name: nfs_fileupdate
 *
 * Description:
 *   This is to update the file attributes like size, type. This sends a LOOKUP msg of nfs
 *   to get latest file attributes.
 *
 * Returned Value:
 *   0 on success; a positive errno value on failure.
 *
 ****************************************************************************/

static int nfs_fileupdate(FAR struct nfsmount *nmp, struct nfsnode *np,
        FAR const char *relpath)
{
    struct file_handle fhandle;
    struct nfs_fattr   fattr;
    int                error = 0;

    /* Find the NFS node associate with the path */

    error = nfs_findnode(nmp, relpath, &fhandle, &fattr, (struct nfs_fattr *)NULL);
    if (error != OK)
    {
        fdbg("ERROR: nfs_findnode returned: %d\n", error);
        return error;
    }

    np->n_fhsize = fhandle.length;
    /* Update the file handle */
    (void)memcpy_s(&np->n_fhandle, np->n_fhsize, &fhandle.handle, fhandle.length);

    /* Save the file attributes */
    nfs_attrupdate(np, &fattr);

    return OK;
}


/****************************************************************************
 * Name: nfs_open
 *
 * Description:
 *   If oflags == O_CREAT it creates a file, if not it check to see if the
 *   type is ok and that deletion is not in progress.
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_open(FAR struct file *filep, FAR const char *relpath,
        int oflags, mode_t mode)
{
    struct nfsmount *nmp;
    struct nfsnode *np;
    int error;

    /* Sanity checks */

    DEBUGASSERT(filep->f_inode != NULL);

    /* Get the mountpoint inode reference from the file structure and the
     * mountpoint private data from the inode structure
     */

    nmp = (struct nfsmount*)filep->f_inode->i_private;
    DEBUGASSERT(nmp != NULL);

    /* Pre-allocate the file private data to describe the opened file. */

    np = (struct nfsnode *)malloc(sizeof(struct nfsnode));
    if (np == NULL)
    {
        fdbg("ERROR: Failed to allocate private data\n");
        return -ENOMEM;
    }

    (void)memset_s(np, sizeof(struct nfsnode), 0, sizeof(struct nfsnode));

    /* Check if the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("ERROR: nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Try to open an existing file at that path */

    error = nfs_fileopen(nmp, np, relpath, oflags, mode);
    if (error != OK)
    {
        /* An error occurred while trying to open the existing file. Check if
         * the open failed because the file does not exist.  That is not
         * necessarily an error; that may only mean that we have to create the
         * file.
         */

        if (error != ENOENT)
        {
            fdbg("ERROR: nfs_findnode failed: %d\n", error);
            goto errout_with_semaphore;
        }

        /* The file does not exist. Check if we were asked to create the file.  If
         * the O_CREAT bit is set in the oflags then we should create the file if it
         * does not exist.
         */

        if ((oflags & O_CREAT) == 0)
        {
            /* Return ENOENT if the file does not exist and we were not asked
             * to create it.
             */

            fdbg("ERROR: File does not exist\n");
            error = ENOENT;
            goto errout_with_semaphore;
        }

        /* Create the file */

        error = nfs_filecreate(nmp, np, relpath, oflags, mode);
        if (error != OK)
        {
            fdbg("ERROR: nfs_filecreate failed: %d\n", error);
            goto errout_with_semaphore;
        }
    }

    /* Initialize the file private data (only need to initialize
     * non-zero elements)
     */

    np->n_crefs = 1;

    /* Attach the private data to the struct file instance */

    filep->f_priv = np;

    /* Then insert the new instance at the head of the list in the mountpoint
     * tructure. It needs to be there (1) to handle error conditions that effect
     * all files, and (2) to inform the umount logic that we are busy.  We
     * cannot unmount the file system if this list is not empty!
     */

    np->n_next   = nmp->nm_file;
    nmp->nm_file = np;

    np->n_flags |= (NFSNODE_OPEN | NFSNODE_MODIFIED);
    np->n_oflags = oflags;

    if(filep->f_oflags & O_APPEND)
    {
        np->n_fpos = np->n_size;
    }
    else
    {
        np->n_fpos = 0;
    }
    filep->f_pos = np->n_fpos;
    np->n_filep = filep;

    nfs_semgive(nmp);
    return OK;

errout_with_semaphore:
    if (np != NULL)
    {
        free(np);
    }

    nfs_semgive(nmp);
    return -error;
}

/****************************************************************************
 * Name: nfs_close
 *
 * Description:
 *   Close a file.
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_close(FAR struct file *filep)
{
    FAR struct nfsmount *nmp;
    FAR struct nfsnode  *np;
    FAR struct nfsnode  *prev;
    FAR struct nfsnode  *curr;
    int ret;

    /* Sanity checks */

    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);

    /* Recover our private data from the struct file instance */

    nmp = (struct nfsmount*) filep->f_inode->i_private;
    np  = (struct nfsnode*) filep->f_priv;

    DEBUGASSERT(nmp != NULL);

    /* Get exclusive access to the mount structure. */

    nfs_semtake(nmp);

    /* Decrement the reference count.  If the reference count would not
     * decrement to zero, then that is all we have to do.
     */

    if (np->n_crefs > 1)
    {
        np->n_crefs--;
        ret = OK;
    }

    /* There are no more references to the file structure.  Now we need to
     * free up all resources associated with the open file.
     *
     * First, find our file structure in the list of file structures
     * containted in the mount structure.
     */

    else
    {
        /* Assume file structure will not be found.  This should never happen. */

        ret = -EINVAL;

        for (prev = (struct nfsnode  *)NULL, curr = nmp->nm_file;
                curr;
                prev = curr, curr = curr->n_next)
        {
            /* Check if this node is ours */

            if (np == curr)
            {
                /* Yes.. remove it from the list of file structures */

                if (prev)
                {
                    /* Remove from mid-list */

                    prev->n_next = np->n_next;
                }
                else
                {
                    /* Remove from the head of the list */

                    nmp->nm_file= np->n_next;
                }

                /* Then deallocate the file structure and return success */

                free(np);
                filep->f_priv = NULL;
                ret = OK;
                break;
            }
        }
    }

    nfs_semgive(nmp);
    return ret;
}

/****************************************************************************
 * Name: nfs_read
 *
 * Returned Value:
 *   The (non-negative) number of bytes read on success; a negated errno
 *   value on failure.
 *
 ****************************************************************************/

static ssize_t nfs_read(FAR struct file *filep, char *buffer, size_t buflen)
{
    FAR struct nfsmount       *nmp;
    FAR struct nfsnode        *np;
    struct rpc_reply_read     *read_response;
    size_t                     readsize;
    size_t                     tmp;
    size_t                     bytesread;
    size_t                     reqlen;
    FAR uint32_t              *ptr;
    int                        error = 0;

    fvdbg("Read %d bytes from offset %d\n", buflen, filep->f_pos);
    /* Sanity checks */

    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);

    /* Recover our private data from the struct file instance */

    nmp = (struct nfsmount*)filep->f_inode->i_private;
    np  = (struct nfsnode*)filep->f_priv;

    DEBUGASSERT(nmp != NULL);

    /* Make sure that the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    fvdbg("File attributes before update, file size %d, file type %d\n", np->n_size, np->n_type);

    error = nfs_fileupdate(nmp, np, filep->f_relpath);
    if (error != OK)
    {
        fdbg("nfs_fileupdate failed: %d\n", error);
        goto errout_with_semaphore;
    }

    fvdbg("File attributes after update, file size %d, file type %d\n", np->n_size, np->n_type);

    /* Get the number of bytes left in the file and truncate read count so that
     * it does not exceed the number of bytes left in the file.
     */

    tmp = np->n_size - filep->f_pos;
    if (buflen > tmp)
    {
        buflen = tmp;
        fvdbg("Read size truncated to %d\n", buflen);
    }

    /* Now loop until we fill the user buffer (or hit the end of the file) */

    for (bytesread = 0; bytesread < buflen; )
    {
        /* Make sure that the attempted read size does not exceed the RPC maximum */

        readsize = buflen - bytesread;
        if (readsize > nmp->nm_rsize)
        {
            readsize = nmp->nm_rsize;
        }

        /* Make sure that the attempted read size does not exceed the IO buffer size */

        tmp = SIZEOF_rpc_reply_read(readsize);
        if (tmp > nmp->nm_buflen)
        {
            readsize -= (tmp - nmp->nm_buflen);
        }

        /* Initialize the request */

        ptr     = (FAR uint32_t*)&(nmp->nm_msgbuffer.read);
        ptr    += sizeof(struct rpc_call_header)/4;
        reqlen  = 0;

        /* Copy the variable length, file handle */
        *ptr++  = txdr_unsigned((uint32_t)np->n_fhsize);
        reqlen += sizeof(uint32_t);

        (void)memcpy_s(ptr, np->n_fhsize, np->n_fhandle.fh_bytes, np->n_fhsize);
        reqlen += (int)np->n_fhsize;
        ptr    += uint32_increment((int)np->n_fhsize);

        /* Copy the file offset */
        txdr_hyper((uint64_t)filep->f_pos, ptr); /*lint !e572 !e571*/
        ptr += 2;
        reqlen += 2*sizeof(uint32_t);

        /* Set the readsize */
        *ptr = txdr_unsigned(readsize);
        reqlen += sizeof(uint32_t);

        /* Perform the read */

        fvdbg("Reading %d bytes\n", readsize);
        nfs_statistics(NFSPROC_READ);
        error = nfs_request(nmp, NFSPROC_READ,
                (FAR void *)&nmp->nm_msgbuffer.read, reqlen,
                (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
        if (error)
        {
            fdbg("ERROR: nfs_request failed: %d\n", error);
            goto errout_with_semaphore;
        }

        read_response = (struct rpc_reply_read *)nmp->nm_iobuffer;
        readsize = fxdr_unsigned(uint32_t, read_response->read.hdr.count);
        /* Copy the read data into the user buffer */

        (void)memcpy_s(buffer, readsize, (const void *)read_response->read.data, readsize);

        /* Update the read state data */

        filep->f_pos += readsize;
        np->n_fpos    += readsize;
        bytesread    += readsize;
        buffer       += readsize;

        /* Check if we hit the end of file */

        if (read_response->read.hdr.eof!= 0)
        {
            break;
        }
    }
    fvdbg("Read %d bytes\n", bytesread);
    nfs_semgive(nmp);
    return bytesread;

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}

/****************************************************************************
 * Name: nfs_write
 *
 * Returned Value:
 *   The (non-negative) number of bytes written on success; a negated errno
 *   value on failure.
 *
 ****************************************************************************/

static ssize_t nfs_write(FAR struct file *filep, const char *buffer,
        size_t buflen)
{
    struct nfsmount       *nmp;
    struct nfsnode        *np;
    loff_t                f_pos;
    size_t                writesize;
    size_t                bufsize;
    size_t                byteswritten;
    size_t                 reqlen;
    FAR uint32_t          *ptr;
    uint32_t               tmp;
    /*  TODO: dead code here, comment it instead of deletion, need further check
        int                    commit = 0;
        */
    int                    committed = NFSV3WRITE_UNSTABLE;
    int                    error;

    /* Sanity checks */

    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);

    /* Recover our private data from the struct file instance */

    nmp = (struct nfsmount*)filep->f_inode->i_private;
    np  = (struct nfsnode*)filep->f_priv;

    DEBUGASSERT(nmp != NULL);

    /* Make sure that the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    if(filep->f_oflags & O_APPEND)
    {
        if(nfs_fileupdate(nmp, np, filep->f_relpath) == OK)
            f_pos = np->n_size;
        else
        {
            error = EAGAIN;
            goto errout_with_semaphore;
        }
    }
    else
        f_pos = np->n_fpos;


    /* Check if the file size would exceed the range of off_t */

    if (np->n_size + buflen < np->n_size)
    {
        error = EFBIG;
        goto errout_with_semaphore;
    }

    fvdbg("Write %d bytes to offset %d\n", buflen, np->n_fpos);
    /* Now loop until we send the entire user buffer */

    writesize = 0;
    for (byteswritten = 0; byteswritten < buflen; )
    {
        /* Make sure that the attempted write size does not exceed the RPC maximum */

        writesize = buflen - byteswritten;
        if (writesize > nmp->nm_wsize)
        {
            writesize = nmp->nm_wsize;
        }
        /* Make sure that the attempted read size does not exceed the IO buffer size */

        bufsize = SIZEOF_rpc_call_write(writesize);
        if (bufsize > nmp->nm_buflen)
        {
            writesize -= (bufsize - nmp->nm_buflen);
        }

        /* Initialize the request.  Here we need an offset pointer to the write
         * arguments, skipping over the RPC header.  Write is unique among the
         * RPC calls in that the entry RPC calls messasge lies in the I/O buffer
         */
        ptr     = (FAR uint32_t *)nmp->nm_iobuffer;
        ptr    += sizeof(struct rpc_call_header)/4; /*lint !e415 !e416*/
        reqlen  = 0;

        /* Copy the variable length, file handle */

        *ptr++  = txdr_unsigned((uint32_t)np->n_fhsize); /*lint !e415 !e416*/
        reqlen += sizeof(uint32_t);

        (void)memcpy_s(ptr, np->n_fhsize, &np->n_fhandle, np->n_fhsize);
        reqlen += (int)np->n_fhsize;
        ptr    += uint32_increment((int)np->n_fhsize);

        /* Copy the file offset */

        txdr_hyper((uint64_t)f_pos, ptr); /*lint !e572 !e571*/
        ptr    += 2;
        reqlen += 2*sizeof(uint32_t);

        /* Copy the count and stable values */

        *ptr++  = txdr_unsigned(writesize);
        *ptr++  = txdr_unsigned((uint32_t)committed);
        reqlen += 2*sizeof(uint32_t);

        /* Copy a chunk of the user data into the I/O buffer */

        *ptr++  = txdr_unsigned(writesize);
        reqlen += sizeof(uint32_t);
        (void)memcpy_s(ptr, writesize, buffer, writesize);
        reqlen += uint32_alignup(writesize);

        /* Perform the write */

        nfs_statistics(NFSPROC_WRITE);
        error = nfs_request(nmp, NFSPROC_WRITE,
                (FAR void *)nmp->nm_iobuffer, reqlen,
                (FAR void *)&nmp->nm_msgbuffer.write, sizeof(struct rpc_reply_write));
        if (error)
        {
            fdbg("ERROR: nfs_request failed: %d\n", error);
            goto errout_with_semaphore;
        }

        /* Get a pointer to the WRITE reply data */

        ptr = (FAR uint32_t *)&nmp->nm_msgbuffer.write.write;

        /* Parse file_wcc.  First, check if WCC attributes follow. */

        tmp = *ptr++;
        if (tmp != 0)
        {
            /* Yes.. WCC attributes follow.  But we just skip over them. */

            ptr += uint32_increment(sizeof(struct wcc_attr));
        }

        /* Check if normal file attributes follow */

        tmp = *ptr++;
        if (tmp != 0)
        {
            /* Yes.. Update the cached file status in the file structure. */

            nfs_attrupdate(np, (FAR struct nfs_fattr *)ptr);
            ptr += uint32_increment(sizeof(struct nfs_fattr));
        }

        /* Get the count of bytes actually written */

        tmp = fxdr_unsigned(uint32_t, *ptr);
        ptr++;

        if (tmp < 1 || tmp > writesize)
        {
            error = EIO;
            goto errout_with_semaphore;
        }

        writesize = tmp;
        f_pos += writesize;
        filep->f_pos = f_pos;
        np->n_fpos = f_pos;
        /* Update the read state data */
        if(filep->f_pos > (loff_t)np->n_size)
            np->n_size = f_pos;
        byteswritten += writesize;
        buffer       += writesize;
    }

    nfs_semgive(nmp);
    return byteswritten;

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}


static off_t   nfs_seek(FAR struct file *filep, off_t offset, int whence)
{
    FAR struct nfsmount       *nmp;
    FAR struct nfsnode        *np;
    int                        error = 0;
    off_t                      position;

    fvdbg("Seek started, offset %d whence %d current file pos %lld\n", offset, whence, filep->f_pos);

    /* Sanity checks */

    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);

    /* Recover our private data from the struct file instance */

    nmp = (struct nfsmount*)filep->f_inode->i_private;
    np  = (struct nfsnode*)filep->f_priv;

    DEBUGASSERT(nmp != NULL);

    /* Make sure that the mount is still healthy */
    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }


    filep->f_pos = np->n_fpos;
    switch (whence)
    {
        case SEEK_SET: /* The offset is set to offset bytes. */
            position = offset;
            break;

        case SEEK_CUR: /* The offset is set to its current location plus offset bytes. */
            position = offset + (off_t)np->n_fpos;
            break;

        case SEEK_END: /* The offset is set to the size of the file plus offset bytes. */
            position = offset + np->n_size;
            break;

        default:
            error = EINVAL;
            goto errout_with_semaphore;
    }

    /* Attempts to set the position beyound the end of file will
     * work if the file is open for write access.
     */
    if ((position > (off_t)np->n_size) && ((np->n_oflags & O_WRONLY) == 0) && ((np->n_oflags & O_RDWR) == 0))
    {
        position = np->n_size;
    }

    /* position less than 0 should be reset to 0 */
    if (position < 0)
    {
        position = 0;
    }

    np->n_fpos = (loff_t)position;
    filep->f_pos = np->n_fpos;
    if (position > (off_t)np->n_size)
    {
        np->n_size = (loff_t)position;
    }

    fvdbg("Seek finished, offset %d whence %d current file pos %lld\n", offset, whence, filep->f_pos);
    nfs_semgive(nmp);
    return (off_t)filep->f_pos;

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}

static loff_t   nfs_seek64(FAR struct file *filep, loff_t offset, int whence)
{
    FAR struct nfsmount       *nmp;
    FAR struct nfsnode        *np;
    int                        error = 0;
    loff_t                     position;

    fvdbg("Seek started, offset %d whence %d current file pos %d\n", offset, whence, filep->f_pos);

    /* Sanity checks */

    DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);

    /* Recover our private data from the struct file instance */

    nmp = (struct nfsmount*)filep->f_inode->i_private;
    np  = (struct nfsnode*)filep->f_priv;

    DEBUGASSERT(nmp != NULL);

    /* Make sure that the mount is still healthy */
    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    filep->f_pos = np->n_fpos;
    switch (whence)
    {
        case SEEK_SET: /* The offset is set to offset bytes. */
            position = offset;
            break;

        case SEEK_CUR: /* The offset is set to its current location plus offset bytes. */
            position = offset + np->n_fpos;
            break;

        case SEEK_END: /* The offset is set to the size of the file plus offset bytes. */
            position = offset + np->n_size;
            break;

        default:
            error = EINVAL;
            goto errout_with_semaphore;
    }

    /* Attempts to set the position beyound the end of file will
     * work if the file is open for write access.
     */
    if ((position > (off_t)np->n_size) && ((np->n_oflags & O_WRONLY) == 0) && ((np->n_oflags & O_RDWR) == 0))
    {
        position = np->n_size;
    }

    /* position less than 0 should be reset to 0 */
    if (position < 0)
    {
        position = 0;
    }

    np->n_fpos = position;
    filep->f_pos = np->n_fpos;
    if (position > (loff_t)np->n_size)
    {
        np->n_size = (uint64_t)position;
    }

    fvdbg("Seek finished, offset %d whence %d current file pos %d\n", offset, whence, filep->f_pos);
    nfs_semgive(nmp);
    return filep->f_pos;

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}

/****************************************************************************
 * Name: binfs_dup
 *
 * Description:
 *   Duplicate open file data in the new file structure.
 *
 ****************************************************************************/

static int nfs_dup(FAR const struct file *oldp, FAR struct file *newp)
{
    struct nfsmount *nmp;
    FAR struct nfsnode *np;
    int error;

    fvdbg("Dup %p->%p\n", oldp, newp);

    /* Sanity checks */

    DEBUGASSERT(oldp->f_priv != NULL && oldp->f_inode != NULL);

    /* Recover our private data from the struct file instance */

    nmp = (struct nfsmount*)oldp->f_inode->i_private;
    np  = (struct nfsnode*)oldp->f_priv;

    DEBUGASSERT(nmp != NULL);

    /* Check if the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("ERROR: nfs_checkmount failed: %d\n", error);
        nfs_semgive(nmp);
        return -error;
    }

    /* Increment the reference count on the NFS node structure */

    DEBUGASSERT(np->n_crefs < 0xff);
    np->n_crefs++;

    /* And save this as the file data for the new node */
    newp->f_priv = np;

    nfs_semgive(nmp);
    return OK;
}

/****************************************************************************
 * Name: nfs_opendir
 *
 * Description:
 *   Open a directory for read access
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_opendir(struct inode *mountpt, const char *relpath,
        struct fs_dirent_s *dir)
{
    struct nfsmount *nmp;
    struct file_handle fhandle;
    struct nfs_fattr obj_attributes;
    struct nfsdir_s *nfs_dir;
    uint32_t objtype;
    int error;

    fvdbg("relpath: \"%s\"\n", relpath ? relpath : "NULL");
    /* Sanity checks */

    DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && dir);

    /* Recover our private data from the inode instance */

    nmp = (struct nfsmount *)mountpt->i_private;

    /* Make sure that the mount is still healthy */
    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("ERROR: nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Find the NFS node associate with the path */
    error = nfs_findnode(nmp, relpath, &fhandle, &obj_attributes, (struct nfs_fattr *)NULL);
    if (error != OK)
    {
        fdbg("ERROR: nfs_findnode failed: %d\n", error);
        goto errout_with_semaphore;
    }
    /* The entry is a directory */

    objtype = fxdr_unsigned(uint32_t, obj_attributes.fa_type);
    if (objtype != NFDIR)
    {
        fdbg("ERROR:  Not a directory, type=%d\n", objtype);
        error = ENOTDIR;
        goto errout_with_semaphore;
    }

    /* Save the directory information in struct fs_dirent_s so that it can be
     * used later when readdir() is called.
     */
    nfs_dir = (struct nfsdir_s *)malloc(sizeof(struct nfsdir_s));
    if (NULL == nfs_dir)
    {
        error = ENOSPC;
        goto errout_with_semaphore;
    }

    nfs_dir->nfs_fhsize = (uint8_t)fhandle.length;
    nfs_dir->nfs_cookie[0] = 0;
    nfs_dir->nfs_cookie[1] = 0;

    DEBUGASSERT(fhandle.length <= DIRENT_NFS_MAXHANDLE);
    (void)memcpy_s(nfs_dir->nfs_fhandle, DIRENT_NFS_MAXHANDLE, &fhandle.handle, DIRENT_NFS_MAXHANDLE);

    dir->u.fs_dir = (fs_dir_s)nfs_dir;
    error = OK;

    nfs_dir->nfs_next = nmp->nm_dir;
    nmp->nm_dir = nfs_dir;
    nfs_dir->nfs_dir = dir;
    nfs_dir->nfs_entries = NULL;

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}

static int nfs_closedir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir)
{
    struct nfsmount *nmp;
    FAR struct nfsdir_s  *prev;
    FAR struct nfsdir_s  *curr;
    struct nfsdir_s *nfs_dir;
    struct entry3 *entry_pos;
    int ret;

    /* Sanity checks */

    DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && dir);

    /* Recover our private data from the inode instance */

    nmp = (struct nfsmount *)mountpt->i_private;

    nfs_dir = (struct nfsdir_s *)(dir->u.fs_dir);

    DEBUGASSERT(nmp != NULL);

    /* Get exclusive access to the mount structure. */

    nfs_semtake(nmp);

    for(entry_pos = nfs_dir->nfs_entries; entry_pos != NULL; entry_pos = nfs_dir->nfs_entries)
    {
        nfs_dir->nfs_entries = entry_pos->next;
        NFS_DIR_ENTRY_FREE(entry_pos);
    }

    /* Assume file structure will not be found.  This should never happen. */

    ret = -EINVAL;

    for (prev = (struct nfsdir_s  *)NULL, curr = nmp->nm_dir;
            curr;
            prev = curr, curr = curr->nfs_next)
    {
        /* Check if this node is ours */

        if (nfs_dir == curr)
        {
            /* Yes.. remove it from the list of file structures */

            if (prev != NULL)
            {
                /* Remove from mid-list */

                prev->nfs_next = nfs_dir->nfs_next;
            }
            else
            {
                /* Remove from the head of the list */

                nmp->nm_dir= nfs_dir->nfs_next;
            }

            /* Then deallocate the file structure and return success */

            free(nfs_dir);
            nfs_dir = NULL;
            ret = OK;
            break;
        }
    }
    nfs_semgive(nmp);

    return ret; /*lint !e438*/
}

/****************************************************************************
 * Name: nfs_readdir
 *
 * Description: Read from directory
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
{
    struct nfsmount *nmp = (struct nfsmount *)NULL;
    struct file_handle fhandle;
    struct nfs_fattr obj_attributes;
    struct nfsdir_s *nfs_dir = (struct nfsdir_s *)NULL;
    struct entry3 *entry = (struct entry3 *)NULL;
    struct entry3 *entry_pos = (struct entry3 *)NULL;
    uint32_t cookies[2];
    uint32_t tmp;
    uint32_t *ptr = (uint32_t *)NULL;
    size_t d_name_size;
    int reqlen;
    int error = 0;

    fvdbg("Entry\n");

    /* Sanity checks */

    DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);

    /* Recover our private data from the inode instance */

    nmp = (struct nfsmount *)mountpt->i_private;

    /* Make sure that the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("ERROR: nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Request a block directory entries, copying directory information from
     * the dirent structure.
     */
    nfs_dir = (struct nfsdir_s *)dir->u.fs_dir;
    cookies[0] = 0;
    cookies[1] = 0;

    if(nfs_dir->nfs_entries == NULL)
    {
        entry_pos = nfs_dir->nfs_entries;
        do
        {
            ptr     = (FAR uint32_t*)&nmp->nm_msgbuffer.readdir.readdir;
            reqlen  = 0;

            /* Copy the variable length, directory file handle */


            *ptr++  = txdr_unsigned((uint32_t)nfs_dir->nfs_fhsize);
            reqlen += sizeof(uint32_t);

            (void)memcpy_s(ptr, nfs_dir->nfs_fhsize, nfs_dir->nfs_fhandle, nfs_dir->nfs_fhsize);
            reqlen += (int)nfs_dir->nfs_fhsize;
            ptr    += uint32_increment((int)nfs_dir->nfs_fhsize);

            /* Cookie and cookie verifier */

            ptr[0] = cookies[0];
            ptr[1] = cookies[1];
            ptr    += 2;
            reqlen += 2*sizeof(uint32_t);

            (void)memcpy_s(ptr, DIRENT_NFS_VERFLEN, nfs_dir->nfs_verifier, DIRENT_NFS_VERFLEN);
            ptr    += uint32_increment(DIRENT_NFS_VERFLEN);
            reqlen += DIRENT_NFS_VERFLEN;

            /* Number of directory entries (We currently only process one entry at a time) */

            *ptr    = txdr_unsigned((uint32_t)nmp->nm_readdirsize);
            reqlen += sizeof(uint32_t);

            /* And read the directory */

            nfs_statistics(NFSPROC_READDIR);
            error = nfs_request(nmp, NFSPROC_READDIR,
                    (FAR void *)&nmp->nm_msgbuffer.readdir, reqlen,
                    (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
            if (error != OK)
            {
                fdbg("ERROR: nfs_request failed: %d\n", error);
                goto errout_with_semaphore;
            }

            /* A new group of entries was successfully read.  Process the
             * information contained in the response header.  This information
             * includes:
             *
             * 1) Attributes follow indication - 4 bytes
             * 2) Directory attributes         - sizeof(struct nfs_fattr)
             * 3) Cookie verifier              - NFSX_V3COOKIEVERF bytes
             * 4) Values follows indication    - 4 bytes
             */
            ptr = (uint32_t *)&((FAR struct rpc_reply_readdir *)nmp->nm_iobuffer)->readdir;

            /* Check if attributes follow, if 0 so Skip over the attributes */
            tmp = *ptr++;
            if (tmp != 0)
            {
                /* Attributes are not currently used */

                ptr += uint32_increment(sizeof(struct nfs_fattr));
            }


            /* Save the verification cookie */
            (void)memcpy_s(nfs_dir->nfs_verifier, DIRENT_NFS_VERFLEN, ptr, DIRENT_NFS_VERFLEN);
            ptr += uint32_increment(DIRENT_NFS_VERFLEN);

            /* Check if values follow.  If no values follow, then the EOF indication
             * will appear next.
             */
            tmp = *ptr++;
            if (tmp == 0)
            {
                /* No values follow, then the reply should consist only of a 4-byte
                 * end-of-directory indication.
                 */

                tmp = *ptr++;
                if (tmp != 0)
                {
                    fvdbg("End of directory\n");
                    error = ENOENT;
                }

                /* What would it mean if there were not data and we not at the end of
                 * file?
                 */

                else
                {
                    fvdbg("No data but not end of directory?\n");
                    error = EAGAIN;
                }

                goto errout_with_semaphore;
            }


            /* If we are not at the end of the directory listing, then a set of entries
             * will follow the header.  Each entry is of the form:
             *
             *    File ID (8 bytes)
             *    Name length (4 bytes)
             *    Name string (varaiable size but in multiples of 4 bytes)
             *    Cookie (8 bytes)
             *    next entry (4 bytes)
             */
            do
            {
                NFS_DIR_ENTRY_MALLOC(entry);

                /* There is an entry. Skip over the file ID and point to the length */
                entry->file_id[0] = *ptr++;
                entry->file_id[1] = *ptr++;/*lint !e662 !e661*/


                /* Get the length and point to the name */
                tmp    = *ptr++; /*lint !e662 !e661*/
                entry->name_len = fxdr_unsigned(uint32_t, tmp);
                entry->contents = (uint8_t*)malloc(entry->name_len + 1);
                if (entry->contents == NULL)
                {
                    free(entry);
                    entry = NULL;
                    goto errout_with_memory;
                }
                (void)memset_s(entry->contents, entry->name_len + 1, 0, entry->name_len + 1);

                (void)strncpy_s((char *)entry->contents, entry->name_len + 1, (const char *)ptr, entry->name_len);

                /* Increment the pointer past the name (allowing for padding). ptr
                 * now points to the cookie.
                 */
                ptr += uint32_increment(entry->name_len);

                /* Save the cookie and increment the pointer to the next entry */
                entry->cookie[0] = *ptr++;
                entry->cookie[1] = *ptr++;

                /* Get the file attributes associated with this name and return
                 * the file type.
                 */
                if(strcmp((char *)entry->contents, ".") == 0 || strcmp((char *)entry->contents, "..") == 0)
                {
                    NFS_DIR_ENTRY_FREE(entry);
                    continue;
                }

                if (nfs_dir->nfs_entries == NULL)
                {
                    entry_pos = entry;
                    nfs_dir->nfs_entries = entry;
                }
                else
                {
                    entry_pos->next = entry;
                    entry_pos = entry;
                }
            } while(*ptr++);
            if (entry_pos != NULL)
            {
                cookies[0] = entry_pos->cookie[0];
                cookies[1] = entry_pos->cookie[1];
            }

        }while(!(*ptr));

        fvdbg("End of directory\n");
        if (nfs_dir->nfs_entries == NULL)
        {
            error = ENOENT;
            goto errout_with_semaphore;
        }

        NFS_DIR_ENTRY_MALLOC(entry);

        /* There is an entry. Skip over the file ID and point to the length */
        entry->file_id[0] = (uint32_t)EOF;
        entry_pos->next = entry; /*lint !e613*/
        entry_pos = entry;

    }

    entry_pos = nfs_dir->nfs_entries;
    if(nfs_dir->nfs_entries->file_id[0] == (uint32_t)EOF)
    {
        fvdbg("End of directory\n");
        error = ENOENT;
        free(nfs_dir->nfs_entries);
        nfs_dir->nfs_entries = NULL;
        goto errout_with_semaphore;
    }

    d_name_size = sizeof(dir->fd_dir.d_name);
    (void)memcpy_s(dir->fd_dir.d_name, d_name_size, (const char *)entry_pos->contents, entry_pos->name_len); /*lint !e670*/
    if (entry_pos->name_len >= d_name_size)
        dir->fd_dir.d_name[d_name_size - 1] = '\0';
    else
        dir->fd_dir.d_name[entry_pos->name_len] = '\0';

    nfs_dir->nfs_entries = entry_pos->next;
    NFS_DIR_ENTRY_FREE(entry_pos);

    fvdbg("name: \"%s\"\n", dir->fd_dir.d_name);
    fhandle.length = (uint32_t)nfs_dir->nfs_fhsize;
    (void)memcpy_s(&fhandle.handle, DIRENT_NFS_MAXHANDLE, nfs_dir->nfs_fhandle, DIRENT_NFS_MAXHANDLE);

    error = nfs_lookup(nmp, dir->fd_dir.d_name, &fhandle, &obj_attributes, (struct nfs_fattr *)NULL);
    if (error != OK)
    {
        fdbg("nfs_lookup failed: %d\n", error);
        goto errout_with_memory;
    }

    /* Set the dirent file type */
    tmp = fxdr_unsigned(uint32_t, obj_attributes.fa_type);
    switch (tmp)
    {
        default:
        case NFNON:        /* Unknown type */
        case NFSOCK:       /* Socket */
        case NFLNK:        /* Symbolic link */
            break;

        case NFREG:        /* Regular file */
            dir->fd_dir.d_type = DT_REG;
            break;

        case NFDIR:        /* Directory */
            dir->fd_dir.d_type = DT_DIR;
            break;

        case NFBLK:        /* Block special device file */
            dir->fd_dir.d_type = DT_BLK;
            break;

        case NFFIFO:       /* Named FIFO */
        case NFCHR:        /* Character special device file */
            dir->fd_dir.d_type = DT_CHR;
            break;
    }
    dir->fd_dir.d_off = dir->fd_position;
    fvdbg("type: %d->%d\n", (int)tmp, dir->fd_dir.d_type);

    nfs_semgive(nmp);
    return OK;

errout_with_memory:
    for(entry_pos = nfs_dir->nfs_entries; entry_pos != NULL; entry_pos = nfs_dir->nfs_entries)
    {
        nfs_dir->nfs_entries = entry_pos->next;
        NFS_DIR_ENTRY_FREE(entry_pos);
    }
errout_with_semaphore:
    nfs_semgive(nmp);
    return -error; /*lint !e438*/
}

/****************************************************************************
 * Name: nfs_rewinddir
 *
 * Description:
 *  Reset the directory traveral logic to the first entry in the open
 *  directory.
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir)
{
    struct nfsmount *nmp;
    struct nfsdir_s *nfs_dir;
    struct entry3 *entry_pos;
    fvdbg("Entry\n");

    /* Sanity checks */
    DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && dir != NULL);

    nmp = (struct nfsmount *)mountpt->i_private;
    nfs_semtake(nmp);

    /* Reset the NFS-specific portions of dirent structure, retaining only the
     * file handle.
     */
    nfs_dir = (struct nfsdir_s *)dir->u.fs_dir;
    (void)memset_s(nfs_dir->nfs_verifier, DIRENT_NFS_VERFLEN, 0, DIRENT_NFS_VERFLEN);
    nfs_dir->nfs_cookie[0] = 0;
    nfs_dir->nfs_cookie[1] = 0;
    for(entry_pos = nfs_dir->nfs_entries; entry_pos != NULL; entry_pos = nfs_dir->nfs_entries)
    {
        nfs_dir->nfs_entries = entry_pos->next;
        NFS_DIR_ENTRY_FREE(entry_pos);
    }
    nfs_semgive(nmp);
    return OK;
}
/****************************************************************************
 * Name: nfs_decode_args
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void nfs_decode_args(FAR struct nfs_mount_parameters *nprmt,
        FAR struct nfs_args *argp)
{
    int maxio;

    /* Get the selected timeout value */
    if ((argp->flags & NFSMNT_TIMEO) != 0 && argp->timeo > 0)
    {
        uint32_t tmp = ((uint32_t)argp->timeo * NFS_HZ + 5) / 10;
        if (tmp < NFS_MINTIMEO)
        {
            tmp = NFS_MINTIMEO;
        }
        else if (tmp > NFS_MAXTIMEO)
        {
            tmp = NFS_MAXTIMEO;
        }
        nprmt->timeo = tmp;
    }

    /* Get the selected retransmission count */

    if ((argp->flags & NFSMNT_RETRANS) != 0 && argp->retrans > 1)
    {
        if  (argp->retrans < NFS_MAXREXMIT)
        {
            nprmt->retry = argp->retrans;
        }
        else
        {
            nprmt->retry = NFS_MAXREXMIT;
        }
    }

    if ((argp->flags & NFSMNT_SOFT) == 0)
    {
        nprmt->retry = NFS_MAXREXMIT + 1;  /* Past clip limit */
    }

    /* Get the maximum amount of data that can be transferred in one packet */

    if ((argp->sotype == SOCK_DGRAM) != 0)
    {
        maxio = NFS_MAXDGRAMDATA;
    }
    else
    {
        maxio = NFS_MAXDATA;
    }

    /* Get the maximum amount of data that can be transferred in one write transfer */

    if ((argp->flags & NFSMNT_WSIZE) != 0 && argp->wsize > 0)
    {
        nprmt->wsize = argp->wsize;

        /* Round down to multiple of blocksize */

        nprmt->wsize &= ~(NFS_FABLKSIZE - 1);
        if (nprmt->wsize <= 0)
        {
            nprmt->wsize = NFS_FABLKSIZE;
        }
    }

    if (nprmt->wsize > maxio)
    {
        nprmt->wsize = maxio;
    }

    /* Get the maximum amount of data that can be transferred in one read transfer */

    if ((argp->flags & NFSMNT_RSIZE) != 0 && argp->rsize > 0)
    {
        nprmt->rsize = argp->rsize;

        /* Round down to multiple of blocksize */

        nprmt->rsize &= ~(NFS_FABLKSIZE - 1);
        if (nprmt->rsize <= 0)
        {
            nprmt->rsize = NFS_FABLKSIZE;
        }
    }

    if (nprmt->rsize > maxio)
    {
        nprmt->rsize = maxio;
    }

    /* Get the maximum amount of data that can be transferred in directory transfer */

    if ((argp->flags & NFSMNT_READDIRSIZE) != 0 && argp->readdirsize > 0)
    {
        nprmt->readdirsize = argp->readdirsize;

        /* Round down to multiple of blocksize */

        nprmt->readdirsize &= ~(NFS_DIRBLKSIZ - 1);
        if (nprmt->readdirsize < NFS_DIRBLKSIZ)
        {
            nprmt->readdirsize = NFS_DIRBLKSIZ;
        }
    }
    else if (argp->flags & NFSMNT_RSIZE)
    {
        nprmt->readdirsize = nprmt->rsize;
    }

    if (nprmt->readdirsize > maxio)
    {
        nprmt->readdirsize = maxio;
    }
}

/****************************************************************************
 * Name: nfs_bind
 *
 * Description:
 *  This implements a portion of the mount operation. This function allocates
 *  and initializes the mountpoint private data and gets mount information
 *  from the NFS server.  The final binding of the private data (containing
 *  NFS server mount information) to the  mountpoint is performed by mount().
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_bind(FAR struct inode *blkdriver, FAR const void *data,
        FAR void **handle, FAR const char *relpath)
{
    FAR struct nfs_args        *argp = (FAR struct nfs_args *)data;
    FAR struct nfsmount        *nmp;
    struct rpcclnt             *rpc;
    struct rpc_call_fs          getattr;
    struct rpc_reply_getattr    resok;
    struct nfs_mount_parameters nprmt;
    uint32_t                    buflen;
    uint32_t                    tmp;
    int                         error = 0;

    DEBUGASSERT(data && handle);

    /* Set default values of the parameters.  These may be overridden by
     * settings in the argp->flags.
     */

    nprmt.timeo       = (uint8_t)NFS_TIMEO;
    nprmt.retry       = NFS_RETRANS;
    nprmt.wsize       = NFS_WSIZE;
    nprmt.rsize       = NFS_RSIZE;
    nprmt.readdirsize = NFS_READDIRSIZE;

    nfs_decode_args(&nprmt, argp);

    /* Determine the size of a buffer that will hold one RPC data transfer.
     * First, get the maximum size of a read and a write transfer */

    buflen = SIZEOF_rpc_call_write(nprmt.wsize);
    tmp    = SIZEOF_rpc_reply_read(nprmt.rsize);

    /* The buffer size will be the maximum of those two sizes */

    if (tmp > buflen)
    {
        buflen = tmp;
    }

    /* But don't let the buffer size exceed the MSS of the socket type.
     *
     * In the case where there are multiple network devices with different
     * link layer protocols (CONFIG_NET_MULTILINK), each network device
     * may support a different UDP MSS value.  Here we arbitrarily select
     * the minimum MSS for that case.
     */
    /* Create an instance of the mountpt state structure */
    nmp = (FAR struct nfsmount *)malloc(SIZEOF_nfsmount(buflen));
    if (!nmp)
    {
        fdbg("ERROR: Failed to allocate mountpoint structure\n");
        return -ENOMEM;
    }

    (void)memset_s(nmp, SIZEOF_nfsmount(buflen), 0, SIZEOF_nfsmount(buflen));

    /* Save the allocated I/O buffer size */

    nmp->nm_buflen = (uint16_t)buflen;

    nmp->nm_so = -1;

    /* Initialize the allocated mountpt state structure. */

    /* Initialize the semaphore that controls access.  The initial count
     * is zero, but nfs_semgive() is called at the completion of initialization,
     * incrementing the count to one.
     */

    (void)sem_init(&nmp->nm_sem, 0, 0);     /* Initialize the semaphore that controls access */

    /* Initialize NFS */

    nfs_true = txdr_unsigned(TRUE);
    nfs_false = txdr_unsigned(FALSE);

    rpcclnt_init();

    /* Set initial values of other fields */

    nmp->nm_timeo       = nprmt.timeo;
    nmp->nm_retry       = nprmt.retry;
    nmp->nm_wsize       = nprmt.wsize;
    nmp->nm_rsize       = nprmt.rsize;
    nmp->nm_readdirsize = nprmt.readdirsize;
    nmp->nm_fhsize      = NFSX_V3FHMAX;

    (void)strncpy_s(nmp->nm_path, sizeof(nmp->nm_path), argp->path, 89);
    (void)memcpy_s(&nmp->nm_nam, sizeof(struct sockaddr), &argp->addr, argp->addrlen);

    /* Set up the sockets and per-host congestion */

    nmp->nm_sotype  = argp->sotype;

    if (nmp->nm_sotype == SOCK_DGRAM || nmp->nm_sotype == SOCK_STREAM)
    {
        /* Connection-less... connect now */

        /* Create an instance of the rpc state structure */

        rpc = (struct rpcclnt *)malloc(sizeof(struct rpcclnt));
        if (rpc == NULL)
        {
            fdbg("ERROR: Failed to allocate rpc structure\n");
            error = ENOMEM;
            goto bad;
        }

        (void)memset_s(rpc, sizeof(struct rpcclnt), 0, sizeof(struct rpcclnt));

        fvdbg("Connecting\n");

        /* Translate nfsmnt flags -> rpcclnt flags */

        rpc->rc_path       = nmp->nm_path;
        rpc->rc_name       = &nmp->nm_nam;
        rpc->rc_sotype     = nmp->nm_sotype;
        rpc->rc_retry      = nmp->nm_retry;
        rpc->rc_so = -1;

        nmp->nm_rpcclnt    = rpc;

        error = rpcclnt_connect(nmp->nm_rpcclnt);
        if (error != OK)
        {
            fdbg("ERROR: nfs_connect failed: %d\n", error);
            goto bad;
        }
    }

    nmp->nm_mounted        = true;
    nmp->nm_so             = nmp->nm_rpcclnt->rc_so;
    nmp->nm_file           = NULL;
    nmp->nm_dir            = NULL;

    (void)memcpy_s(&nmp->nm_fh, sizeof(nfsfh_t),
        &nmp->nm_rpcclnt->rc_fh, sizeof(nfsfh_t));
    nmp->nm_fhsize = nmp->nm_rpcclnt->rc_fh_len;

    /* Get the file attributes */

    getattr.fs.fsroot.length = txdr_unsigned((uint32_t)nmp->nm_fhsize);
    (void)memcpy_s(&getattr.fs.fsroot.handle, sizeof(nfsfh_t),
        &nmp->nm_fh, sizeof(nfsfh_t));

    error = nfs_request(nmp, NFSPROC_GETATTR,
            (FAR void *)&getattr, /*sizeof(struct FS3args)*/ (sizeof(getattr.fs.fsroot.length) + nmp->nm_fhsize),
            (FAR void*)&resok, sizeof(struct rpc_reply_getattr));
    if (error)
    {
        fdbg("ERROR: nfs_request failed: %d\n", error);
        goto bad;
    }

    /* Save the file attributes */

    (void)memcpy_s(&nmp->nm_fattr, sizeof(struct nfs_fattr),
        &resok.attr, sizeof(struct nfs_fattr));

    /* Mounted! */

    *handle = (void*)nmp;
    nfs_semgive(nmp);

    fvdbg("Successfully mounted\n");
    return OK;

bad:
    if (nmp)
    {
        /* Disconnect from the server */

        if (nmp->nm_rpcclnt)
        {
            rpcclnt_disconnect(nmp->nm_rpcclnt);
            free(nmp->nm_rpcclnt);
            nmp->nm_rpcclnt = NULL;
        }

        /* Free connection-related resources */

        (void)sem_destroy(&nmp->nm_sem);
        free(nmp);
        nmp = NULL;
    }

    return -error;/*lint !e438*/
}

/****************************************************************************
 * Name: nfs_unbind
 *
 * Description: This implements the filesystem portion of the umount
 *   operation.
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

int nfs_unbind(FAR void *handle, FAR struct inode **blkdriver)
{
    FAR struct nfsmount *nmp = (FAR struct nfsmount *)handle;
    int error;

    fvdbg("Entry\n");
    DEBUGASSERT(nmp);

    /* Get exclusive access to the mount structure */

    nfs_semtake(nmp);

    /* Are there any open files?  We can tell if there are open files by looking
     * at the list of file structures in the mount structure.  If this list
     * not empty, then there are open files and we cannot unmount now (or a
     * crash is sure to follow).
     */

    if (nmp->nm_file != NULL || nmp->nm_dir != NULL)
    {
        fdbg("ERROR;  There are open files or directories\n");

        /* This implementation currently only supports unmounting if there are
         * no open file references.
         */

        error = EBUSY;
        goto errout_with_semaphore;
    }

    /* No open file... Umount the file system. */

    error = rpcclnt_umount(nmp->nm_rpcclnt);
    if (error)
    {
        fdbg("ERROR: rpcclnt_umount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Disconnect from the server */

    rpcclnt_disconnect(nmp->nm_rpcclnt);

    /* And free any allocated resources */

    (void)sem_destroy(&nmp->nm_sem);
    free(nmp->nm_rpcclnt);
    nmp->nm_rpcclnt = NULL;
    free(nmp);
    nmp = NULL;

    return -error;/*lint !e438*/

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}

/****************************************************************************
 * Name: nfs_fsinfo
 *
 * Description:
 *   Return information about root directory.
 *
 * Returned Value:
 *   0 on success; positive errno value on failure
 *
 * Assumptions:
 *   The caller has exclusive access to the NFS mount structure
 *
 ****************************************************************************/
int nfs_fsinfo(FAR struct nfsmount *nmp)
{
    struct rpc_call_fs fsinfo;
    struct rpc_reply_fsinfo repfsp;
    struct nfs_fsinfo *fsp;
    uint32_t pref;
    uint32_t max;
    int error = 0;

    fsinfo.fs.fsroot.length = txdr_unsigned((uint32_t)nmp->nm_fhsize);
    fsinfo.fs.fsroot.handle = nmp->nm_fh;

    /* Request FSINFO from the server */

    nfs_statistics(NFSPROC_FSINFO);
    error = nfs_request(nmp, NFSPROC_FSINFO,
            (FAR void *)&fsinfo, sizeof(struct FS3args),
            (FAR void *)&repfsp, sizeof(struct rpc_reply_fsinfo));
    if (error)
    {
        return error;
    }

    if (txdr_unsigned(repfsp.fsinfo.obj_attributes.obj_attribute_follow) == 1)
    {
        fsp = (struct nfs_fsinfo *)&repfsp.fsinfo.fs_rtmax;
    }
    else
    {
        fsp = (struct nfs_fsinfo *)((void *)(&repfsp.fsinfo.obj_attributes.attributes));
    }

    /* Save the root file system attributes */

    pref = fxdr_unsigned(uint32_t, fsp->fs_wtpref);
    if (pref < nmp->nm_wsize)
    {
        nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) & ~(NFS_FABLKSIZE - 1);
    }

    max = fxdr_unsigned(uint32_t, fsp->fs_wtmax);
    if (max < nmp->nm_wsize)
    {
        nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1);
        if (nmp->nm_wsize == 0)
        {
            nmp->nm_wsize = max;
        }
    }

    pref = fxdr_unsigned(uint32_t, fsp->fs_rtpref);
    if (pref < nmp->nm_rsize)
    {
        nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) & ~(NFS_FABLKSIZE - 1);
    }

    max = fxdr_unsigned(uint32_t, fsp->fs_rtmax);
    if (max < nmp->nm_rsize)
    {
        nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1);
        if (nmp->nm_rsize == 0)
        {
            nmp->nm_rsize = max;
        }
    }

    pref = fxdr_unsigned(uint32_t, fsp->fs_dtpref);
    if (pref < nmp->nm_readdirsize)
    {
        nmp->nm_readdirsize = (pref + NFS_DIRBLKSIZ - 1) & ~(NFS_DIRBLKSIZ - 1);
    }

    if (max < nmp->nm_readdirsize)
    {
        nmp->nm_readdirsize = max & ~(NFS_DIRBLKSIZ - 1);
        if (nmp->nm_readdirsize == 0)
        {
            nmp->nm_readdirsize = max;
        }
    }

    return OK;
}
/****************************************************************************
 * Name: nfs_statfs
 *
 * Description:
 *   Return filesystem statistics
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_statfs(FAR struct inode *mountpt, FAR struct statfs *sbp)
{
    FAR struct nfsmount *nmp;
    FAR struct rpc_call_fs *fsstat;
    FAR struct rpc_reply_fsstat *sfp;
    FAR struct nfs_statfs_ctx  *stfp;
    int error = 0;
    uint64_t tquad;

    /* Sanity checks */

    DEBUGASSERT(mountpt && mountpt->i_private);

    /* Get the mountpoint private data from the inode structure */

    nmp = (struct nfsmount*)mountpt->i_private;

    /* Check if the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("ERROR: nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Fill in the statfs info */

    sbp->f_type = NFS_SUPER_MAGIC;

    error = nfs_fsinfo(nmp);
    if (error)
    {
        fdbg("ERROR: nfs_fsinfo failed: %d\n", error);
        goto errout_with_semaphore;
    }

    fsstat = &nmp->nm_msgbuffer.fsstat;
    fsstat->fs.fsroot.length = txdr_unsigned((uint32_t)nmp->nm_fhsize);
    (void)memcpy_s(&fsstat->fs.fsroot.handle, sizeof(nfsfh_t),
        &nmp->nm_fh, sizeof(nfsfh_t));

    nfs_statistics(NFSPROC_FSSTAT);
    error = nfs_request(nmp, NFSPROC_FSSTAT,
            (FAR void *)fsstat, sizeof(struct FS3args),
            (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
    if (error)
    {
        goto errout_with_semaphore;
    }

    sfp                   = (FAR struct rpc_reply_fsstat *)nmp->nm_iobuffer;
    if (txdr_unsigned(sfp->fsstat.attributes_follow) == 1)
    {
        stfp = (FAR struct nfs_statfs_ctx *)&sfp->fsstat.sf_tbytes;
    }
    else
    {
        stfp = (FAR struct nfs_statfs_ctx *)&sfp->fsstat.obj_attributes;
    }

    sbp->f_bsize          = NFS_FABLKSIZE;
    tquad                 = fxdr_hyper(&stfp->sf_tbytes); /*lint !e571*/
    sbp->f_blocks         = tquad / (uint64_t) NFS_FABLKSIZE;
    tquad                 = fxdr_hyper(&stfp->sf_fbytes); /*lint !e571*/
    sbp->f_bfree          = tquad / (uint64_t) NFS_FABLKSIZE;
    tquad                 = fxdr_hyper(&stfp->sf_abytes); /*lint !e571*/
    sbp->f_bavail         = tquad / (uint64_t) NFS_FABLKSIZE;
    tquad                 = fxdr_hyper(&stfp->sf_tfiles); /*lint !e571*/
    sbp->f_files          = tquad;
    tquad                 = fxdr_hyper(&stfp->sf_ffiles); /*lint !e571*/
    sbp->f_ffree          = tquad;
    sbp->f_namelen        = NAME_MAX;

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}

/****************************************************************************
 * Name: nfs_remove
 *
 * Description:
 *   Remove a file
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_remove(struct inode *mountpt, const char *relpath)
{
    FAR struct nfsmount    *nmp;
    struct file_handle      fhandle;
    struct nfs_fattr        fattr;
    char                    filename[NAME_MAX + 1];
    FAR uint32_t           *ptr;
    size_t                  namelen;
    int                     reqlen;
    int                     error;

    /* Sanity checks */

    DEBUGASSERT(mountpt && mountpt->i_private);

    /* Get the mountpoint private data from the inode structure */

    nmp = (struct nfsmount*)mountpt->i_private;

    /* Check if the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("ERROR: nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Find the NFS node of the directory containing the file to be deleted */

    error = nfs_finddir(nmp, relpath, &fhandle, &fattr, filename);
    if (error != OK)
    {
        fdbg("ERROR: nfs_finddir returned: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Create the REMOVE RPC call arguments */

    ptr    = (FAR uint32_t *)&nmp->nm_msgbuffer.removef.remove;
    reqlen = 0;

    /* Copy the variable length, directory file handle */

    *ptr++  = txdr_unsigned(fhandle.length);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, fhandle.length, &fhandle.handle, fhandle.length);
    reqlen += (int)fhandle.length;
    ptr    += uint32_increment(fhandle.length);

    /* Copy the variable-length file name */

    namelen = strlen(filename);

    *ptr++  = txdr_unsigned(namelen);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, namelen, filename, namelen);
    reqlen += uint32_alignup(namelen);

    /* Perform the REMOVE RPC call */

    nfs_statistics(NFSPROC_REMOVE);
    error = nfs_request(nmp, NFSPROC_REMOVE,
            (FAR void *)&nmp->nm_msgbuffer.removef, reqlen,
            (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}

/****************************************************************************
 * Name: nfs_mkdir
 *
 * Description:
 *   Create a directory
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode)
{
    struct nfsmount       *nmp;
    struct file_handle     fhandle;
    struct nfs_fattr       fattr;
    char                   dirname[NAME_MAX + 1];
    FAR uint32_t          *ptr;
    uint32_t               tmp;
    size_t                 namelen;
    int                    reqlen;
    int                    error;

    /* Sanity checks */

    DEBUGASSERT(mountpt && mountpt->i_private);

    /* Get the mountpoint private data from the inode structure */

    nmp = (struct nfsmount*) mountpt->i_private;

    /* Check if the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("ERROR: nfs_checkmount: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Find the NFS node of the directory containing the directory to be created */

    error = nfs_finddir(nmp, relpath, &fhandle, &fattr, dirname);
    if (error != OK)
    {
        fdbg("ERROR: nfs_finddir returned: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Format the MKDIR call message arguments */

    ptr    = (FAR uint32_t *)&nmp->nm_msgbuffer.mkdir.mkdir;
    reqlen = 0;

    /* Copy the variable length, directory file handle */

    *ptr++  = txdr_unsigned(fhandle.length);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, fhandle.length, &fhandle.handle, fhandle.length);
    ptr    += uint32_increment(fhandle.length);
    reqlen += (int)fhandle.length;

    /* Copy the variable-length directory name */

    namelen = strlen(dirname);

    *ptr++  = txdr_unsigned(namelen);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, namelen, dirname, namelen);
    ptr    += uint32_increment(namelen);
    reqlen += uint32_alignup(namelen);

    /* Set the mode.  NOTE: Here we depend on the fact that the NuttX and NFS
     * bit settings are the same (at least for the bits of interest).
     */

    *ptr++  = nfs_true; /* True: mode value follows */
    reqlen += sizeof(uint32_t);

    if (!mode)
    {
        mode = (NFSMODE_IXOTH | NFSMODE_IROTH |
                NFSMODE_IXGRP | NFSMODE_IRGRP |
                NFSMODE_IXUSR | NFSMODE_IWUSR | NFSMODE_IRUSR);
    }
    tmp = mode & (NFSMODE_IXOTH | NFSMODE_IWOTH | NFSMODE_IROTH |
            NFSMODE_IXGRP | NFSMODE_IWGRP | NFSMODE_IRGRP |
            NFSMODE_IXUSR | NFSMODE_IWUSR | NFSMODE_IRUSR);
    *ptr++  = txdr_unsigned(tmp);
    reqlen += sizeof(uint32_t);

    /* Set the user ID to zero */

    *ptr++  = nfs_true;             /* True: Uid value follows */
    *ptr++  = 0;                    /* UID = 0 (nobody) */
    reqlen += 2*sizeof(uint32_t);

    /* Set the group ID to one */

    *ptr++  = nfs_true;            /* True: Gid value follows */
    *ptr++  = htonl(1);            /* GID = 1 (nogroup) */
    reqlen += 2*sizeof(uint32_t);

    /* No size */

    *ptr++  = nfs_false; /* False: No size value follows */
    reqlen += sizeof(uint32_t);

    /* Don't change times */

    *ptr++  = htonl(NFSV3SATTRTIME_DONTCHANGE); /* Don't change atime */
    *ptr++  = htonl(NFSV3SATTRTIME_DONTCHANGE); /* Don't change mtime */
    reqlen += 2*sizeof(uint32_t);

    /* Perform the MKDIR RPC */

    nfs_statistics(NFSPROC_MKDIR);
    error = nfs_request(nmp, NFSPROC_MKDIR,
            (FAR void *)&nmp->nm_msgbuffer.mkdir, reqlen,
            (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
    if (error)
    {
        fdbg("ERROR: nfs_request failed: %d\n", error);
    }

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}

/****************************************************************************
 * Name: nfs_rmdir
 *
 * Description:
 *   Remove a directory
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_rmdir(struct inode *mountpt, const char *relpath)
{
    struct nfsmount       *nmp;
    struct file_handle     fhandle;
    struct nfs_fattr       fattr;
    char                   dirname[NAME_MAX + 1];
    FAR uint32_t          *ptr;
    size_t                 namelen;
    int                    reqlen;
    int                    error;

    /* Sanity checks */

    DEBUGASSERT(mountpt && mountpt->i_private);

    /* Get the mountpoint private data from the inode structure */

    nmp = (struct nfsmount *)mountpt->i_private;

    /* Check if the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("ERROR: nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Find the NFS node of the directory containing the directory to be removed */

    error = nfs_finddir(nmp, relpath, &fhandle, &fattr, dirname);
    if (error != OK)
    {
        fdbg("ERROR: nfs_finddir returned: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Set up the RMDIR call message arguments */

    ptr    = (FAR uint32_t *)&nmp->nm_msgbuffer.rmdir.rmdir;
    reqlen = 0;

    /* Copy the variable length, directory file handle */

    *ptr++  = txdr_unsigned(fhandle.length);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, fhandle.length, &fhandle.handle, fhandle.length);
    reqlen += (int)fhandle.length;
    ptr    += uint32_increment(fhandle.length);

    /* Copy the variable-length directory name */

    namelen = strlen(dirname);

    *ptr++  = txdr_unsigned(namelen);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, namelen, dirname, namelen);
    reqlen += uint32_alignup(namelen);

    /* Perform the RMDIR RPC */

    nfs_statistics(NFSPROC_RMDIR);
    error = nfs_request(nmp, NFSPROC_RMDIR,
            (FAR void *)&nmp->nm_msgbuffer.rmdir, reqlen,
            (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}
static int nfs_getfilename(char *dstpath, unsigned int dstpathLen, const char *srcpath)
{
    int uwRet = 0;
    char*  path = (char*)srcpath + strlen(srcpath) - 1;
    while(path && *path-- != '/');
    uwRet = strncpy_s(dstpath, dstpathLen, path + 2, strlen(path + 2));
    if (uwRet != EOK)
    {
       return ENAMETOOLONG;
    }
    return OK;
}
static int nfs_compare_type(struct nfsmount *nmp, const char *oldrelpath,
        const char *newrelpath)
{
    struct file_handle fhandle;
    struct nfs_fattr   stOld_attributes;
    struct nfs_fattr   stNew_attributes;
    int ret;
    const char *ch1 = oldrelpath,*ch2 = newrelpath;

    while(*ch1 == *ch2)
    {
        if (*ch1 == '\0' || *ch2 == '\0')
            break;
        ch1++;
        ch2++;
    }

    if ((*ch1 == '/' && *ch2 == '\0') || (*ch2 == '/' && *ch1 == '\0'))
        return 1;

    ret = nfs_findnode(nmp, oldrelpath, &fhandle, &stOld_attributes, (struct nfs_fattr *)NULL);
    if (ret != OK && ret != ENOTDIR)
    {
        fdbg("ERROR: old path nfs_findnode failed: %d\n", ret);
        return 1;
    }
    ret = nfs_findnode(nmp, newrelpath, &fhandle, &stNew_attributes, (struct nfs_fattr *)NULL);
    if (ret != OK)
    {
        fdbg("ERROR: new path nfs_findnode failed: %d\n", ret);
        return 0;
    }

    /* if two value are equal which should return 0;it is not equal should return 1*/
    return stOld_attributes.fa_type != stNew_attributes.fa_type;
}

/****************************************************************************
 * Name: nfs_rename
 *
 * Description:
 *   Rename a file or directory
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_rename(struct inode *mountpt, const char *oldrelpath,
        const char *newrelpath)
{
    struct nfsmount        *nmp;
    struct nfsnode         *np = NULL;
    struct nfsdir_s        *nd = NULL;
    struct file_handle      from_handle;
    struct file_handle      to_handle;
    char                    directory[NAME_MAX+1];
    char                    from_name[NAME_MAX+1];
    char                    to_name[NAME_MAX+1];
    char                   *fullpath = (char *)NULL;
    char                   *relpath = NULL;
    struct nfs_fattr        fattr;
    FAR uint32_t           *ptr;
    size_t                  namelen;
    int                     reqlen;
    int                     error;

    /* Sanity checks */

    DEBUGASSERT(mountpt && mountpt->i_private);

    /* Get the mountpoint private data from the inode structure */

    nmp = (struct nfsmount *)mountpt->i_private;

    /* Check if the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("ERROR: nfs_checkmount returned: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Find the NFS node of the directory containing the 'from' object */

    error = nfs_finddir(nmp, oldrelpath, &from_handle, &fattr, from_name);
    if (error != OK)
    {
        fdbg("ERROR: nfs_finddir returned: %d\n", error);
        goto errout_with_semaphore;
    }
    fdbg("nfs_finddir oldrelpath:%s, fattr->fa_type:%x, from_name: %s\n",
        oldrelpath, fattr.fa_type, from_name);

    for(nd = nmp->nm_dir; nd; nd = nd->nfs_next)
    {
        char   filename[50] = {};
        error = nfs_getfilename(filename, sizeof(filename), nd->nfs_dir->fd_dir.d_name);
        if (error != OK)
        {
          goto errout_with_semaphore;
        }
        if (strcmp(filename, from_name) == 0)
        {
            break;
        }
    }
    for(np = nmp->nm_file; np; np = np->n_next)
    {
        char   filename[50] = {};
        error = nfs_getfilename(filename, sizeof(filename), np->n_filep->f_relpath);
        if (error != OK)
        {
           goto errout_with_semaphore;
        }
        if (strcmp((const char*)filename, from_name) == 0)
        {
            break;
        }
    }

    if(np == NULL && nd == NULL)
    {
        fdbg("INFO: not opened\n");
    }

    /* Find the NFS node of the directory containing the 'from' object */

    error = nfs_finddir(nmp, newrelpath, &to_handle, &fattr, to_name);
    if (error != OK)
    {
        fdbg("ERROR: nfs_finddir returned: %d\n", error);
        goto errout_with_semaphore;
    }
    fdbg("nfs_finddir newrelpath:%s, fattr->fa_type:%x, to_name: %s\n",
        newrelpath, fattr.fa_type, to_name);

    /* compare new and old type */
    error = nfs_compare_type(nmp,oldrelpath,newrelpath);
    if(error != OK)
    {
        fdbg("ERROR: nfs_compare_type returned: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Format the RENAME RPC arguments */

    ptr    = (FAR uint32_t *)&nmp->nm_msgbuffer.renamef.rename;
    reqlen = 0;

    /* Copy the variable length, 'from' directory file handle */

    *ptr++  = txdr_unsigned(from_handle.length);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, from_handle.length, &from_handle.handle, from_handle.length);
    reqlen += (int)from_handle.length;
    ptr    += uint32_increment(from_handle.length);

    /* Copy the variable-length 'from' object name */

    namelen = strlen(from_name);

    *ptr++  = txdr_unsigned(namelen);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, namelen, from_name, namelen);
    reqlen += uint32_alignup(namelen);
    ptr    += uint32_increment(namelen);

    /* Copy the variable length, 'to' directory file handle */

    *ptr++  = txdr_unsigned(to_handle.length);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, to_handle.length, &to_handle.handle, to_handle.length);
    ptr    += uint32_increment(to_handle.length);
    reqlen += (int)to_handle.length;

    /* Copy the variable-length 'to' object name */

    namelen = strlen(to_name);

    *ptr++  = txdr_unsigned(namelen);
    reqlen += sizeof(uint32_t);

    (void)memcpy_s(ptr, namelen, to_name, namelen);
    reqlen += uint32_alignup(namelen);

    /* Perform the RENAME RPC */

    nfs_statistics(NFSPROC_RENAME);
    error = nfs_request(nmp, NFSPROC_RENAME,
            (FAR void *)&nmp->nm_msgbuffer.renamef, reqlen,
            (FAR void *)nmp->nm_iobuffer, nmp->nm_buflen);
    if (error != OK)
    {
        fdbg("ERROR: nfs_request returned: %d\n", error);
        goto errout_with_semaphore;
    }

    if (np != NULL)
    {
        error =  strncpy_s(directory, NAME_MAX + 1, np->n_filep->f_path, np->n_filep->f_relpath - np->n_filep->f_path);
        directory[np->n_filep->f_relpath - np->n_filep->f_path] = '\0';
        if(error != EOK)
        {
           goto errout_with_semaphore;
        }
        error = vfs_normalize_path(directory, newrelpath, &fullpath);
        if (error < 0)
        {
            error = -error;
            fdbg("ERROR: vfs_normalize_path returned: %d\n", error);
            goto errout_with_semaphore;
        }

        relpath = strstr(fullpath, newrelpath);
        if (relpath == NULL)
        {
            fdbg("ERROR: relpath is not sub in fullpath.\n", error);
            free(fullpath);
            goto errout_with_semaphore;
        }

        /* Update the path of old file. */
        free(np->n_filep->f_path);
        np->n_filep->f_path = fullpath;
        np->n_filep->f_relpath = relpath;
    }
    else if (nd != NULL)
    {
        (void)memcpy_s(nd->nfs_dir->fd_dir.d_name, strlen(to_name) + 1, to_name, strlen(to_name) + 1);
    }

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}

/****************************************************************************
 * Name: nfs_stat
 *
 * Description:
 *   Return information about the file system object at 'relpath'
 *
 * Returned Value:
 *   0 on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int nfs_stat(struct inode *mountpt, const char *relpath,
        struct stat *buf)
{
    struct nfsmount   *nmp;
    struct file_handle fhandle;
    struct nfs_fattr   obj_attributes;
    uint32_t           tmp;
    uint32_t           mode;
    int                error;

    /* Sanity checks */

    DEBUGASSERT(mountpt && mountpt->i_private);

    /* Get the mountpoint private data from the inode structure */

    nmp = (struct nfsmount*)mountpt->i_private;
    DEBUGASSERT(nmp && buf);

    /* Check if the mount is still healthy */

    nfs_semtake(nmp);
    error = nfs_checkmount(nmp);
    if (error != OK)
    {
        fdbg("ERROR: nfs_checkmount failed: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Get the file handle attributes of the requested node */

    error = nfs_findnode(nmp, relpath, &fhandle, &obj_attributes, (struct nfs_fattr *)NULL);
    if (error != OK)
    {
        fdbg("ERROR: nfs_findnode failed: %d\n", error);
        goto errout_with_semaphore;
    }

    /* Construct the file mode.  This is a 32-bit, encoded value containing
     * both the access mode and the file type.
     */

    tmp = fxdr_unsigned(uint32_t, obj_attributes.fa_mode);

    /* Here we exploit the fact that most mode bits are the same in NuttX
     * as in the NFSv3 spec.
     */
    mode = tmp & (NFSMODE_IXOTH|NFSMODE_IWOTH|NFSMODE_IROTH|
            NFSMODE_IXGRP|NFSMODE_IWGRP|NFSMODE_IRGRP|
            NFSMODE_IXUSR|NFSMODE_IWUSR|NFSMODE_IRUSR);

    /* Now OR in the file type */

    tmp = fxdr_unsigned(uint32_t, obj_attributes.fa_type);
    switch (tmp)
    {
        default:
        case NFNON:   /* Unknown type */
            break;

        case NFREG:   /* Regular file */
            mode |= S_IFREG;
            break;

        case NFDIR:   /* Directory */
            mode |= S_IFDIR;
            break;

        case NFBLK:   /* Block special device file */
            mode |= S_IFBLK;
            break;

        case NFCHR:   /* Character special device file */
            mode |= S_IFCHR;
            break;

        case NFLNK:   /* Symbolic link */
            mode |= S_IFLNK;
            break;

        case NFSOCK:  /* Socket */
            mode |= S_IFSOCK;
            break;

        case NFFIFO:  /* Named pipe */
            mode |= S_IFMT;
            break;
    }

    buf->st_mode    = mode;
    buf->st_size    = fxdr_hyper(&obj_attributes.fa_size); /*lint !e571*/
    buf->st_blksize = 0;
    buf->st_blocks  = 0;
    buf->st_mtime   = fxdr_hyper(&obj_attributes.fa_mtime); /*lint !e571*/
    buf->st_atime   = fxdr_hyper(&obj_attributes.fa_atime); /*lint !e571*/
    buf->st_ctime   = fxdr_hyper(&obj_attributes.fa_ctime); /*lint !e571*/

errout_with_semaphore:
    nfs_semgive(nmp);
    return -error;
}


static int nfs_stat64(struct inode *mountpt, const char *relpath,
        struct stat64 *buf)
{
    int result;
    struct stat buff;

    result = nfs_stat(mountpt, relpath, &buff);
    if (!result)
    {
        buf->st_mode    = buff.st_mode;
        buf->st_size    = (long long)buff.st_size;
        buf->st_blksize = 0;
        buf->st_blocks  = 0;
        buf->st_mtime   = buff.st_mtime;
        buf->st_atime   = buff.st_atime;
        buf->st_ctime   = buff.st_ctime;

    }

    return result;
}

int nfs_mount(const char *server_ip_and_path, const char *mount_path,
                    unsigned int uid, unsigned int gid)
{
    struct nfs_args  g_nfs_args = {0};
    char                    *server_ip_addr = (char *)NULL;
    char                    *server_nfs_path = (char *)NULL;
    int                     found_colon = 0;
    unsigned int            pos = 0;
    int                     uwRet = 0;
    struct sockaddr_in      *nfs_srv_addr = (struct sockaddr_in *)NULL;
    size_t len;

    rpcclnt_setuidgid(uid, gid);

    len = strlen(server_ip_and_path);
    for (pos = 0; pos < len; pos++)
    {
        if (*(server_ip_and_path + pos) == ':')
        {
            found_colon = 1;
            break;
        }
    }

    if (!found_colon)
    {
        return -1;
    }

    server_ip_addr = (char *)malloc(pos + 1);
    if (server_ip_addr == NULL)
    {
        PRINTK("malloc failure\n");
        return -1;
    }

    (void)strncpy_s(server_ip_addr, pos + 1, server_ip_and_path, pos);
    *(server_ip_addr + pos) = '\0';
    server_nfs_path = (char *)(server_ip_and_path + pos + 1);

    (void)memset_s(&g_nfs_args, sizeof(g_nfs_args), 0, sizeof(g_nfs_args));

    if (g_NFSMOUNT_HOOK != NULL)
    {
        g_NFSMOUNT_HOOK(&g_nfs_args);
    }

    g_nfs_args.path = server_nfs_path; // server nfs dir

    nfs_srv_addr = (struct sockaddr_in *)&g_nfs_args.addr;
    nfs_srv_addr->sin_family = AF_INET;
    nfs_srv_addr->sin_addr.s_addr = inet_addr(server_ip_addr);
    nfs_srv_addr->sin_port = htons(PMAPPORT);

    g_nfs_args.addrlen = sizeof(g_nfs_args.addr);
#if (NFS_PROTOTYPE == IPPROTO_TCP)/*lint !e553*/
    g_nfs_args.sotype = SOCK_STREAM;
#elif (NFS_PROTOTYPE == IPPROTO_UDP)
    g_nfs_args.sotype = SOCK_DGRAM;
#endif

    PRINTK("Mount nfs on %s:%s, uid:%d, gid:%d\n", server_ip_addr, server_nfs_path, uid, gid);
    uwRet = mount((const char *)NULL, mount_path, "nfs", 0, &g_nfs_args);

    free(server_ip_addr);

    if (uwRet)
    {
        perror("mount nfs error");
        return -1;
    }
    PRINTK("Mount nfs finished.\n");
    return 0;
}

#endif
